/*
 * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <js_native_api.h>
#include <js_native_api_types.h>
#include <string.h>
#include <exception>
#include "allheaders.h"
#include "napi/native_api.h"
#include "tesseract/baseapi.h"
#include "pix_internal.h"

#include "hilog/log.h"

// LOG
static unsigned int logDomain = 0xD001234;
static const char* APP_TAG = "OCR_napi";
#define MYAPP_LOGERR(type, ...) ((void)OH_LOG_Print((type), LOG_ERROR, logDomain, APP_TAG, __VA_ARGS__))

using namespace std;
static tesseract::TessBaseAPI * api;
int initResult = -1;

struct InitOCRAddOnData {
    napi_async_work asyncWork = nullptr;   //异步工作项
    napi_deferred deferred = nullptr;      //用于Promise的resolve,reject
    napi_ref callback = nullptr;           //回调函数
    string args0;                          //第一个参数
    string args1;                          //第二个参数
    int result;                            //业务逻辑处理结果（返回结果）
};

static void executeInitOCR(napi_env env, void* data) {
    InitOCRAddOnData * addonData = (InitOCRAddOnData *)data;
    MYAPP_LOGERR(LOG_APP, "OCR executeInitOCR 111");
    MYAPP_LOGERR(LOG_APP, "OCR executeInitOCR arg0 = %{public}s", (const char*)addonData->args0.c_str());
    MYAPP_LOGERR(LOG_APP, "OCR executeInitOCR arg1 = %{public}s", (const char*)addonData->args1.c_str());

    napi_value resultValue;
    try {
        if (api == nullptr) {
            api = new tesseract::TessBaseAPI();
        }
        initResult = api->Init((const char*)addonData->args0.c_str(), (const char*)addonData->args1.c_str());
        MYAPP_LOGERR(LOG_APP, "OCR executeInitOCR 222 result = %{public}d", initResult);
        addonData->result = initResult;
    } catch (std::exception e) {
        MYAPP_LOGERR(LOG_APP, "OCR executeInitOCR 333 catch error");
        int error = -1;
        if (initResult != 0) {
            error = -1;
        } else {
            error = 0;
        }
        addonData->result = error;
    }
}

static void completeInitOCRForCallback(napi_env env, napi_status status, void * data) {
    MYAPP_LOGERR(LOG_APP, "OCR completeInitOCRForCallback 111");
    InitOCRAddOnData * addonData = (InitOCRAddOnData *)data;
    napi_value callback = nullptr;
    napi_get_reference_value(env, addonData->callback, &callback);
    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);

    //执行回调函数
    napi_value returnVal = nullptr;
    napi_call_function(env, undefined, callback, 1, &result, &returnVal);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static void completeInitOCRForPromise(napi_env env, napi_status status, void * data) {
    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 111");
    InitOCRAddOnData * addonData = (InitOCRAddOnData *)data;
    napi_value result = nullptr;
    napi_create_int32(env, addonData->result, &result);
    napi_resolve_deferred(env, addonData->deferred, result);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 444");
    delete addonData;
}

static napi_value InitOCR(napi_env env, napi_callback_info info) {
    size_t argc = 3;
    napi_value args[3] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);



    char dir[100] = { 0 };
    size_t length2 = 0;
    napi_get_value_string_utf8(env, args[1], dir, 100, &length2);

    if (2 == argc) {
        //Promise方式
        // 创建Promise
        napi_value promise = nullptr;
        napi_deferred deferred = nullptr;
        napi_create_promise(env, &deferred, &promise);


        auto addonData = new InitOCRAddOnData{
            .asyncWork = nullptr,
            .deferred = deferred,
        };

        //3. NAPI类型转成C/C++类型
        char lang[1024] = { 0 };
        size_t length1 = 0;
        napi_get_value_string_utf8(env, args[0], lang, 1024, &length1);
        addonData->args0 = string(lang);

        char dir[1024] = { 0 };
        size_t length2 = 0;
        napi_get_value_string_utf8(env, args[1], dir, 1024, &length2);
        addonData->args1 = string(dir);

        //4. 创建async work
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "initOCR", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeInitOCR, completeInitOCRForPromise, (void *)addonData, &addonData->asyncWork);

        //5. 将创建的async work加入到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);

    } else {
        //Callback方式
        auto addonData = new InitOCRAddOnData{
        .asyncWork = nullptr,
    };
    //3. NAPI类型转成C/C++类型
    char lang[1024] = { 0 };
    size_t length1 = 0;
    napi_get_value_string_utf8(env, args[0], lang, 1024, &length1);

    addonData->args0 = string(lang);

    char dir[1024] = { 0 };
    size_t length2 = 0;
    napi_get_value_string_utf8(env, args[1], dir, 1024, &length2);

    addonData->args1 = string(dir);

    napi_create_reference(env, args[2], 1, &addonData->callback);

    //4. 创建async work
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "initOCR", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, executeInitOCR, completeInitOCRForCallback, (void *)addonData, &addonData->asyncWork);

    //将创建的async work加到队列中，由底层调度执行
    napi_queue_async_work(env, addonData->asyncWork);

    napi_value result = 0;
    napi_get_null(env, &result);

    return result;
    }
}

static napi_value InitOCR_bak(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char lang[100] = { 0 };
    size_t length = 0;
    napi_get_value_string_utf8(env, args[0], lang, 100, &length);

    char dir[100] = { 0 };
    size_t length2 = 0;
    napi_get_value_string_utf8(env, args[1], dir, 100, &length2);

    napi_value initValue;
    try {
        if (api == nullptr) {
            api = new tesseract::TessBaseAPI();
        }
        initResult = api->Init(dir, lang);
        napi_create_int32(env, initResult, &initValue);
    } catch (std::exception e) {
        napi_create_int32(env, -1, &initValue);
    }

    return initValue;
}

static napi_value StartOCR_bak(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char imagePath[100] = { 0 };
    size_t length = 0;
    napi_get_value_string_utf8(env, args[0], imagePath, 100, &length);

    napi_value resultValue;
    try {
        if (api != nullptr) {
            PIX * pix = pixRead(imagePath);
            api->SetImage(pix);

            char * result = api->GetUTF8Text();
            napi_create_string_latin1(env, result, NAPI_AUTO_LENGTH, &resultValue);
            //释放资源
            pixDestroy (& pix);
            delete[] result;
        }
    } catch (std::exception e) {
        std::string error = "Error: ";
        if (initResult != 0) {
            error += "please first init tesseractocr.";
        } else {
            error += e.what();
        }
        napi_create_string_latin1(env, error.c_str(), NAPI_AUTO_LENGTH, &resultValue);
    }

    return resultValue;
}


struct StartOCRAddOnData {
    napi_async_work asyncWork = nullptr;   //异步工作项
    napi_deferred deferred = nullptr;      //用于Promise的resolve,reject
    napi_ref callback = nullptr;           //回调函数
    string args0;                          //第一个参数
    string result;                        //业务逻辑处理结果（返回结果）
};

static void executeStartOCR(napi_env env, void* data) {
    //    OH_LOG_ERROR(LogType::LOG_APP, "executeStartOCR 111");
    StartOCRAddOnData * addonData = (StartOCRAddOnData *)data;
    napi_value resultValue;
    //    OH_LOG_ERROR(LogType::LOG_APP, "executeStartOCR 222");
    try {
        if (api != nullptr) {
            PIX * pix = pixRead((const char*)addonData->args0.c_str());
            api->SetImage(pix);

            char * result = api->GetUTF8Text();
            addonData->result = result;

            //释放资源
            pixDestroy (& pix);
            delete[] result;
        }
    } catch (std::exception e) {
        std::string error = "Error: ";
        if (initResult != 0) {
            error += "please first init tesseractocr.";
        } else {
            error += e.what();
        }
        //        napi_create_string_latin1(env, error.c_str(), NAPI_AUTO_LENGTH, &resultValue);
        addonData->result = error;
    }

}

static void completeStartOCRForCallback(napi_env env, napi_status status, void * data) {
    StartOCRAddOnData * addonData = (StartOCRAddOnData *)data;
    napi_value callback = nullptr;
    napi_get_reference_value(env, addonData->callback, &callback);
    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);
    napi_value result = nullptr;
    napi_create_string_utf8(env, addonData->result.c_str(), addonData->result.length(), &result);

    //执行回调函数
    napi_value returnVal = nullptr;
    napi_call_function(env, undefined, callback, 1, &result, &returnVal);

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
}

static void completeStartOCRForPromise(napi_env env, napi_status status, void * data) {
    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 111");
    StartOCRAddOnData * addonData = (StartOCRAddOnData *)data;
    napi_value result = nullptr;
    napi_create_string_utf8(env, addonData->result.c_str(), addonData->result.length(), &result);
    napi_resolve_deferred(env, addonData->deferred, result);

    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 222");

    //删除napi_ref对象
    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 333");

    //删除异步工作项
    napi_delete_async_work(env, addonData->asyncWork);
    //    OH_LOG_ERROR(LogType::LOG_APP, "completeStartOCRForPromise 444");
    delete addonData;
}



static napi_value StartOCR(napi_env env, napi_callback_info info) {
    OH_LOG_ERROR(LogType::LOG_APP, "OCR StartOCR 111");
    size_t argc = 2;
    napi_value args[2] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    //    char imagePath[100] = { 0 };
    //    size_t length = 0;
    //    napi_get_value_string_utf8(env, args[0], imagePath, 100, &length);


    if (1 == argc) {
        //Promise方式
        // 创建Promise
        napi_value promise = nullptr;
        napi_deferred deferred = nullptr;
        napi_create_promise(env, &deferred, &promise);


        auto addonData = new StartOCRAddOnData{
            .asyncWork = nullptr,
            .deferred = deferred,
        };


        //3. NAPI类型转成C/C++类型
        char imagePath[1024] = { 0 };
        size_t length = 0;
        napi_get_value_string_utf8(env, args[0], imagePath, 1024, &length);

        addonData->args0 = string(imagePath);


        //4. 创建async work
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "startOCR", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeStartOCR, completeStartOCRForPromise, (void *)addonData, &addonData->asyncWork);

        //5. 将创建的async work加入到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);

        return promise;
    } else {
        //Callback方式
        auto addonData = new StartOCRAddOnData{
            .asyncWork = nullptr,
        };
        //3. NAPI类型转成C/C++类型
        char imagePath[1024] = { 0 };
        size_t length = 0;
        napi_get_value_string_utf8(env, args[0], imagePath, 1024, &length);

        addonData->args0 = string(imagePath);

        napi_create_reference(env, args[1], 1, &addonData->callback);

        //4. 创建async work
        napi_value resourceName = nullptr;
        napi_create_string_utf8(env, "startOCR", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(env, nullptr, resourceName, executeStartOCR, completeStartOCRForCallback, (void *)addonData, &addonData->asyncWork);

        //将创建的async work加到队列中，由底层调度执行
        napi_queue_async_work(env, addonData->asyncWork);

        napi_value result = 0;
        napi_get_null(env, &result);

        return result;
}

}

static napi_value StartOCRForBox(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {
nullptr
};

napi_get_cb_info(env, info, & argc, args, nullptr, nullptr);

char imagePath[100] = {
0
};
size_t length = 0;
napi_get_value_string_utf8(env, args[0], imagePath, 100, & length);

napi_value resultValue;
try {
if (api != nullptr) {
PIX * pix = pixRead(imagePath);
api->SetImage(pix);

std::string result;
Boxa * boxes = api->GetComponentImages(tesseract::RIL_TEXTLINE, true, NULL, NULL);
for (int i = 0;
i < boxes->n;
i++) {
BOX * box = boxaGetBox(boxes, i, L_CLONE);
api->SetRectangle(box->x, box->y, box->w, box->h);
char * ocrResult = api->GetUTF8Text();
int conf = api->MeanTextConf();
result = box->x;
result += ",";
result += box->y;
result += ",";
result += box->w;
result += ",";
result += box->h;
result += ",";
result += conf;
result += ",";
result += ocrResult;
result += "<>";
delete[] ocrResult;
}

napi_create_string_latin1(env, result.c_str(), NAPI_AUTO_LENGTH, & resultValue);
//释放资源
pixDestroy(& pix);
}
} catch (std::exception e) {
std::string error = "Error: ";
if (initResult != 0) {
error += "please first init tesseractocr.";
} else {
error += e.what();
}
napi_create_string_latin1(env, error.c_str(), NAPI_AUTO_LENGTH, & resultValue);
}

return resultValue;
}

static napi_value DestroyOCR(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {
nullptr
};

napi_get_cb_info(env, info, & argc, args, nullptr, nullptr);

if (api != nullptr) {
api-> End();
}

return 0;
}

static napi_value GetInitLanguagesAsString(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {
nullptr
};

napi_get_cb_info(env, info, & argc, args, nullptr, nullptr);

napi_value value;
if (api != nullptr) {
const char * result = api-> GetInitLanguagesAsString();
napi_create_string_latin1(env, result, NAPI_AUTO_LENGTH, & value);
}

return value;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{
"initOCR", nullptr, InitOCR, nullptr, nullptr, nullptr, napi_default, nullptr
},
{
"startOCR", nullptr, StartOCR, nullptr, nullptr, nullptr, napi_default, nullptr
},
{
"destroyOCR", nullptr, DestroyOCR, nullptr, nullptr, nullptr, napi_default, nullptr
},
{
"startOCRForBox", nullptr, StartOCRForBox, nullptr, nullptr, nullptr, napi_default, nullptr
},
{
"getInitLanguagesAsString", nullptr, GetInitLanguagesAsString, nullptr, nullptr, nullptr, napi_default, nullptr
}
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END

static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "tesseract",
.nm_priv = ((void *)0),
.reserved = {
0
},
};

extern "C" __attribute__((constructor)) void RegisterHelloModule(void) {
napi_module_register(& demoModule);
}
